\chapter{Python Client API}
\label{chap:api:python-client}

HyperDex provides Python bindings to the client in the module
\code{hyperdex.client}.  This library wraps the HyperDex C client library and
enables the use of native Python data types.

\subsection{Building the HyperDex Python Binding}
\label{sec:api:python-client:building}

The HyperDex Python bindings must be requested at configure time as it is not
automatically built.  You can ensure that the Python bindings are always built
by providing the \code{--enable-python-bindings} option to \code{./configure}
like so:

\begin{consolecode}
% ./configure --enable-client --enable-python-bindings
\end{consolecode}

\subsection{Using Python Within Your Application}
\label{sec:api:python-client:using}

All client operation are defined in the \code{hyperdex.client} module.  You can
access this in your program with:

\begin{pythoncode}
import hyperdex.client
\end{pythoncode}

\subsection{Hello World}
\label{sec:api:python-client:hello-world}

The following is a minimal application that stores the value "Hello World" and
then immediately retrieves the value:

\inputminted{python}{\topdir/python/client/hello-world.py}

You can run this example with:

\begin{consolecode}
% python hello-world.py
put "Hello World!"
got:
{'v': 'Hello World!'}
\end{consolecode}

Right away, there are several points worth noting in this example:

\begin{itemize}
\item Every operation is synchronous.  The put and get operations run to
completion by default.

\item Python types are automatically converted to HyperDex types.  There's no
need to specify information such as the length of each string, as one would do
with the C API.
\end{itemize}

\subsection{Asynchronous Operations}
\label{sec:api:python-client:async-ops}

For convenience, the Python bindings treat every operation as synchronous.  This
enables you to write short scripts without concern for asynchronous operations.
Most operations come with an asynchronous form, denoted by the \code{async\_}
prefix.  For example, the above Hello World example could be rewritten in
asynchronous fashion as such:

\inputminted{python}{\topdir/python/client/hello-world-async-wait.py}

This enables applications to issue multiple requests simultaneously and wait for
their completion in an application-specific order.  It's also possible to use
the \code{loop} method on the client object to wait for the next request to
complete:

\inputminted{python}{\topdir/python/client/hello-world-async-loop.py}

\subsection{Data Structures}
\label{sec:api:python-client:data-structures}

The Python bindings automatically manage conversion of data types from Python to
HyperDex types, enabling applications to be written in idiomatic Python.

\subsubsection{Examples}
\label{sec:api:python-client:examples}

This section shows examples of Python data structures that are recognized by
HyperDex.  The examples here are for illustration purposes and are not
exhaustive.

\paragraph{Strings}

The HyperDex client recognizes Python's strings and automatically converts them
to HyperDex strings.  For example:

\begin{pythoncode}
c.put("kv", "somekey", {"v": "somevalue"})
\end{pythoncode}

\paragraph{Integers}

The HyperDex client recognizes Python's integers and longs and automatically
converts them to HyperDex integers.  For example:

\begin{pythoncode}
c.put("k"', "somekey", {"v": 42})
\end{pythoncode}

\paragraph{Floats}

The HyperDex client recognizes Python's floating point numbers and
automatically converts them to HyperDex floats.  For example:

\begin{pythoncode}
c.put("kv", "somekey", {"v": 3.1415})
\end{pythoncode}

\paragraph{Lists}

The HyperDex client recognizes Python lists and automatically converts them to
HyperDex lists.  For example:

\begin{pythoncode}
c.put("kv", "somekey", {"v1": ["a", "b", "c"]})
c.put("kv", "somekey", {"v2": [1, 2, 3]})
c.put("kv", "somekey", {"v3": [1.0, 0.5, 0.25]})
\end{pythoncode}

\paragraph{Sets}

The HyperDex client recognizes Python sets and automaticaly converts them to
HyperDex sets.  For example:

\begin{pythoncode}
require 'set'
c.put("kv", "somekey", {"v1": set(["a", "b", "c"])})
c.put("kv", "somekey", {"v2": set([1, 2, 3])})
c.put("kv", "somekey", {"v3": set([1.0, 0.5, 0.25])})
\end{pythoncode}

\paragraph{Maps}

The HyperDex client recognizes Python dictionaries and automatically converts them to
HyperDex maps.  For example:

\begin{pythoncode}
c.put("kv", "somekey", {"v1": {"k": "v"}})
c.put("kv", "somekey", {"v2": {1: 2}})
c.put("kv", "somekey", {"v3": {3.14: 0.125}})
c.put("kv", "somekey", {"v3": {"a": 1}})
\end{pythoncode}

\subsection{Attributes}
\label{sec:api:python-client:attributes}

Attributes in Python are specified in the form of a dictionary from attribute names to
their values.  As you can see in the examples above, attributes are specified in
the form:

\begin{pythoncode}
{"name": "value"}
\end{pythoncode}

\subsection{Map Attributes}
\label{sec:api:python-client:map-attributes}

Map attributes in Python are specified in the form of a nested dictionary.  The outer
diction key specifies the name, while the inner diction key-value pair specifies the
key-value pair of the map.  For example:

\begin{pythoncode}
{"name": {"key": "value"}}
\end{pythoncode}

\subsection{Predicates}
\label{sec:api:python-client:predicates}

Predicates in Python are specified in the form of a diction from attribute names to
their predicates.  In the simple case, the predicate is just a value to be
compared against:

\begin{pythoncode}
{"v": "value"}
\end{pythoncode}

This is the same as saying:

\begin{pythoncode}
{"v": hyperdex.client.Equals('value')}
\end{pythoncode}

The Python bindings support the full range of predicates supported by HyperDex
itself.  For example:

\begin{pythoncode}
{"v": hyperdex.client.LessEqual(5)}
{"v": hyperdex.client.GreaterEqual(5)}
{"v": hyperdex.client.Range(5, 10)}
{"v": hyperdex.client.Regex('^s.*')}
{"v": hyperdex.client.LengthEquals(5)}
{"v": hyperdex.client.LengthLessEqual(5)}
{"v": hyperdex.client.LengthGreaterEqual(5)}
{"v": hyperdex.client.Contains('value')}
\end{pythoncode}

\subsection{Error Handling}
\label{sec:api:python-client:error-handling}

All error handling within the Python bindings is done via the exception handling
mechanism of Python.  Errors will be raised by the library and should be handled
by your application.  For example, if we were trying to store an integer (5) as
attribute \code{"v"}, where \code{"v"} is actually a string, we'd generate an error.

\begin{pythoncode}
try:
    c.put("kv", "my_key", {"v": 5})
except HyperDexClientException as e:
    print e.status
    print e.symbol
    print e
\end{pythoncode}

Errors of type \code{HyperDexClientException} will contain both a message
indicating what went wrong, as well as the underlying \code{enum
hyperdex\_client\_returncode}.  The member \code{status} indicates the numeric
value of this enum, while \code{symbol} returns the enum as a string.  The above
code will fail with the following output:

\begin{verbatim}
8525
HYPERDEX_CLIENT_WRONGTYPE
invalid attribute "v": attribute has the wrong type
\end{verbatim}

\subsection{Operations}
\label{sec:api:python-client:ops}

\input{\topdir/python/client/ops}
\pagebreak

\subsection{Working with Signals}
\label{sec:api:python-client:signals}

The HyperDex client library is signal-safe.  Should a signal interrupt the
client during a blocking operation, it will raise a
\code{HyperDexClientException} with status \code{HYPERDEX\_CLIENT\_INTERRUPTED}.

\subsection{Working with Threads}
\label{sec:api:python-client:threads}

The Python module is fully reentrant.  Instances of
\code{hyperdex.client.Client} and their associated state may be accessed from
multiple threads, provided that the application employs its own synchronization
that provides mutual exclusion.

Put simply, a multi-threaded application should protect each \code{Client}
instance with a mutex or lock to ensure correct operation.
